#pragma once
#include"data.hpp"
#include"httplib.h"

extern cloud::DataManager* _data;

namespace cloud
{
    class Service
    {
    private:
        //三种处理方式
        
        //文件上传 加static是因为this指针缘故
        static void Upload(const httplib::Request& req,httplib::Response& rsp)
        {
            // post/upload
            auto ret = req.has_file("file");
            if(ret == false)
            {
                rsp.status = 400;
                return; 
            }

            auto file = req.get_file_value("file"); //返回一个结构体，其中包含文件的名称和内容
            //file.filename //文件名称    file.content//文件数据
            std::string back_dir = Config::GetInstance()->GetBackDir();
            std::string realpath = back_dir + Cloud::FileUtil(file.filename).FileName();
            Cloud::FileUtil fu(realpath);
            fu.SetContent(file.content); //将数据写入文件
            BackupInfo info;
            info.NewBackupInfo(realpath); //组织备份的文件信息
            _data->Insert(info); //向数据管理模块添加备份的文件信息
            return;
        }
        
        static std::string TimetoStr(time_t t)
        {
            //调用ctime函数，用于将时间戳转换为一个表示当地时间的字符串
            std::string tmp = std::ctime(&t);
            return tmp;
        }

        //展示
        static void ListShow(const httplib::Request& req, httplib::Response& rsp)
        {
            //1.获取所有的文件备份信息
            std::vector<BackupInfo> arry;
            _data->GetAll(&arry);
            
            //2.根据所有备份信息，组织html文件数据
            std::stringstream ss;
            ss << "<html><head><title>Download</title></head>";
            ss << "<body><h1>Download</h1><table>";
            for(auto& a : arry)
            {
					ss << "<tr>";
					std::string filename = Cloud::FileUtil(a.real_path).FileName();
					ss << "<td><a href='" << a.url << "'>" << filename << "</a></td>"; //都用双引号会造成字符串冲突，早html中单引号和双引号又一样
					ss << "<td align='right'>" << TimetoStr(a.mtime) << "</td>";
					ss << "<td align='right'>" << a.fsize / 1024 << "k</td>";
					ss << "</tr>";
            }
            ss << "</table></body></html>";
            
            rsp.body = ss.str();
            rsp.set_header("Content-Type", "text/html"); //text/html是为了向浏览器表明这是一个html数据，需要对其渲染展示出卡
            //rsp.set_header("Content-Type","application/octet-stream"); //这种类型通常用于下载文件
            rsp.status = 200;
            return;
        }

        // std::string GetETag(const std::string& filename)
        // {
        //     //etag = filename + fsize + mtime
        //     Cloud::FileUtil fu(filename);
        //     std::string etag = fu.FileName();
        //     etag += "-";
        //     etag += std::to_string(fu.FileSize);
        //     etag += "-";
        //     etag += std::to_string(fu.LastMTime());
        //     return etag;
        // }

        //没有选择上面的方式生成ETag是因为文件被解压后生成了新文件，新文件的最后一次访问时间是新的时间，不是真正意义上最后一次访问的时间，BackupInfo是事先保存好了真正最后一次访问的时间，所以用BackupInfo保存的最后一次访问时间获取ETag
        static std::string GetETag(const BackupInfo& info)
        {
            //etag = filename + fsize + mtime
            Cloud::FileUtil fu(info.real_path);
            std::string etag = fu.FileName();
            etag += "-";
            etag += std::to_string(info.fsize);
            etag += "-";
            etag += std::to_string(info.mtime);
            return etag;
        }

        static void Download(const httplib::Request& req,httplib::Response& rsp)
        {
            //1.获取客户端请求的资源路径 req.path
            //2.根据资源路径获取文件备份信息
            BackupInfo info;
            _data->GetOneByURL(req.path,&info);
            //3.判断文件是否被压缩，如果被压缩就要先解压，删除压缩包并修改备份信息
            if(info.pack_flag == true)
            {
                Cloud::FileUtil fu(info.pack_path);
                fu.UnCompress(info.real_path); //将文件解压缩到备份目录下
                fu.Remove(); //删除压缩包
                info.pack_flag = false;
                _data->Update(info);
            }

            //4.读取文件数据，放入rsp.body中
            Cloud::FileUtil fu(info.real_path);
            fu.GetContent(&rsp.body);
            
            bool retrans = false; //记录是不是断点续传
            std::string old_etag;
            if(req.has_header("If-Range")) //如果请求报文有这个头部字段就说明是断点续传
            {
                old_etag = req.get_header_value("If-Range");
                //有IF-Range头部字段且这个字段的值和请求文件的最近ETag一致则说明符合断点续传
                if( old_etag == GetETag(info) )
                {
                    retrans = true;
                }
            }
              
            if(retrans == false) //没有断点续传标志
            {
                fu.GetContent(&rsp.body);
                //5.设置响应头部字段：ETag，Accept-Ranges:bytes Content-Length自动会设置
                rsp.set_header("Accept-Ranges","bytes");
                rsp.set_header("ETag",GetETag(info));
                rsp.set_header("Content-Type","application/octet-stream"); //这种类型通常用于下载文件
                rsp.status = 200;
            }
            else //httplib库已经实现了断点续传，只需要用户将文件所有数据读取到rsp.body中，内部会自动根据请求区间，从body中取出指定区间数据进行响应
            {
                //std::string range = req.get_header_val("Range")  bytes=start-end
                fu.GetContent(&rsp.body);
                //5.设置响应头部字段：ETag，Accept-Ranges:bytes Content-Length自动会设置
                rsp.set_header("Accept-Ranges","bytes");
                rsp.set_header("ETag",GetETag(info));
                rsp.set_header("Content-Type","application/octet-stream"); //这种类型通常用于下载文件
                //rsp.set_header("Content-Range","bytes start-end/fsize")
                rsp.status = 206; //区间响应状态码是206
            }
        }

    public:
        Service()
        {
            Config* config = Config::GetInstance();
            _server_port = config->GetServerPort();
            _server_ip = config->GetServerIp();
            _download_prefix = config->GetDownloadPrefix();
            
        }

        //运行接口
        bool RunModule()
        {
            _server.Post("./upload",Upload);
            _server.Get("/listshow",ListShow);
            _server.Get("/",ListShow);
            _server.Get("/download/(.*)",Download); //(.*)匹配任意字符任意次
            _server.listen(_server_ip.c_str(),_server_port); //监听
    
        }

    private:
        int _server_port;
        std::string _server_ip;
        std::string _download_prefix; //下载前缀
        httplib::Server _server;
    };
}

